/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.portmgr.core.ip;

import hxg.portmgr.core.device.Device;
import hxg.portmgr.core.device.DeviceUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * @author hxg
 */
public class IpSubnet{

    final String ipSubnet;
    int mask;
    private final int ipSubnetIntValue;
    final static public IpSubnet rootSubnet = new IpSubnet("0.0.0.0", 0);

    private IpSubnet(String ipAddr, int mask) {
        int ipmask = 1;
        ipmask = ipmask << mask;
        ipmask--;
        ipmask = ipmask << (32 - mask);
        ipSubnetIntValue = (int) (IpAddress.getIntForIP(ipAddr) & ipmask);
        ipSubnet = IpAddress.getStringForIP(ipSubnetIntValue);
        this.mask = mask;
    }

    public String getIpSubnet() {
        return ipSubnet;
    }

    public int getMask() {
        return mask;
    }

    @Override
    public String toString() {
        return ipSubnet + "/" + mask;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 53 * hash + this.mask;
        hash = 53 * hash + this.ipSubnetIntValue;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final IpSubnet other = (IpSubnet) obj;
        if (this.mask != other.mask) {
            return false;
        }
        if (this.ipSubnetIntValue != other.ipSubnetIntValue) {
            return false;
        }
        return true;
    }
    
    


    public static IpSubnet getIpSubnet(IpAddress ipAddr) {
        return new IpSubnet(ipAddr.getIpAddr(), ipAddr.getNetMask());
    }

    public static IpSubnet getIpSubnet(String ipAddr, int mask) {
        return new IpSubnet(ipAddr, mask);
    }

    public IpSubnet getUpSubnet() {
        if (mask >= 1) {
            return new IpSubnet(ipSubnet, mask - 1);
        } else {
            return null;
        }
    }

    public boolean isSubnetOf(IpSubnet subnet) {
        int msk = subnet.mask;
        IpSubnet selfUp = new IpSubnet(ipSubnet, msk);
        if (selfUp.equals(subnet)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 该方法存在如下问题：第二次执行时会有null出现 在getIpSubnetNode方法中，由于存在UpNode重定向，所以部分Node丢失
     *
     * @return
     */
    public static Collection<IpSubnetNode> getIpSubnetNodes() {
        Collection<IpSubnetNode> ipSubnetNodes = new HashSet();
        Map<IpSubnet, Collection<Device>> devmp = DeviceUtils.getDeviceIpMap();
        for (IpSubnet ipSubnet : devmp.keySet()) {
            //对所有有设备的IpSubnet建立对应Node
            IpSubnetNode node = IpSubnetNode.getIpSubnetNode(ipSubnet);
            //node.setUpNode(new IpSubnetNode(ipSubnet.getUpSubnet()));
            node.addAllDevice(devmp.get(ipSubnet));
            ipSubnetNodes.add(node);
        }
        HashMap<IpSubnetNode, AtomicInteger> nodeMap = new HashMap<IpSubnetNode, AtomicInteger>();
        //HashMap<IpSubnet, IpSubnetNode> net2node = new HashMap<IpSubnet, IpSubnetNode>();
        /**
         * 建立Node的计数表和对应表
         */
        for (IpSubnetNode node : ipSubnetNodes) {
            nodeMap.put(node, new AtomicInteger(1));
            //net2node.put(node.ipSubnet, node);
        }
        /**
         * 循环计数，掩码最大23位，所以简单的一个32次循环 为什么从32开始？ 由于上级节点的掩码可能比本级小，所以从32开始遍历才不会漏
         */
        for (int i = 32; i >= 1; i--) {
            //在第一次访问该Node时注册UpNode上级节点，由于是按掩码遍历，所以是不会重复访问的。
            /**
             * 做两件事 一、遍历该掩码的Node，同时给该Node标注上其上级Node 二、计算上级Node的应用次数
             */
            for (IpSubnetNode node : IpSubnetNode.getKnownIpSubnetNodes()) {
                if (node.ipSubnet.mask == i) {
                    IpSubnetNode upNode = IpSubnetNode.getIpSubnetNode(node.ipSubnet.getUpSubnet());
                    if (nodeMap.containsKey(upNode)) {
                        nodeMap.get(upNode).getAndIncrement();
                    } else {
                        nodeMap.put(upNode, new AtomicInteger(1));
                    }
                }
            }
            /**
             * 再次遍历，此次遍历的目的在于重指链接
             * 每个Node的上级Node如果仅被引用一次且不包含设备的话，Node的UpNode就会被重新指向UpNode的UpNode
             * 那么第二次时就可能有Null值出现
             */
            for (IpSubnetNode node : IpSubnetNode.getKnownIpSubnetNodes()) {
                if (node.ipSubnet.mask >= i) {
                    IpSubnetNode upNode = node.getUpNode();
                    if (upNode != null) {
                        if (nodeMap.get(upNode).get() == 1 && upNode.getDevices().isEmpty()) {
                            node.setUpNode(upNode.getUpNode());
                            if (nodeMap.containsKey(node.getUpNode())) {
                                nodeMap.get(node.getUpNode()).getAndIncrement();
                            } else {
                                nodeMap.put(upNode, new AtomicInteger(1));
                            }
                        } else {
                            ipSubnetNodes.add(node.getUpNode());
                        }
                    }
                }
            }
        }
        return ipSubnetNodes;
    }

}
